﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Rappelz.GameServer
{
    public class SkillProp : GameObject
    {
        public class SkillPropInfo
        {
            public uint m_nStartTime;
            public uint m_nEndTime;
            public uint m_nInterval;
            public uint m_nLastFireTime;
        }

        public static SkillProp Create(uint caster, Skill pSkill, int nMagicPoint, float fHateRatio)
        {
            return new SkillProp(caster, pSkill, nMagicPoint, fHateRatio);
        }


// Function       :   public void StructSkillProp::~StructSkillProp()
// Function       :   public unsigned int StructSkillProp::GetHandle()
// Function       :   public bool StructSkillProp::ProcDelete()

        public override void onProcess(int nThreadIdx)
        {
            Creature pCaster;

            if ( !this.m_bProcessEnd )
            {
                RLock rl = RappelzServer.Instance._LockObjectWithVisibleRange(this);
                AutoLock al = new AutoLock(rl.handle);

                uint ct = Globals.GetArTime();
                if (ct > this.m_Info.m_nEndTime || this.m_bIsRemovePended )
                {
                    this.m_bProcessEnd = true;
                    pCaster = Creature.get(this.m_hCaster) as Creature;

                    if (pCaster != null)
                    {
                        if (pCaster.GetTrapHandle() == this.m_hHandle )
                            pCaster.m_hTrap = 0;
                    }

                    RappelzServer.Instance.RemoveObject(this);
                    RappelzServer.Instance.DeleteObject(this);
                }
                else
                {
                    if (ct < this.m_Info.m_nLastFireTime + this.m_Info.m_nInterval )
                    {
//                        ArcadiaAutoLock::_ArcadiaAutoLock(&_lock);
                        return;
                    }

                    this.m_Info.m_nLastFireTime = ct;
                    pCaster = Creature.get(this.m_hCaster) as Creature;

                    if (pCaster != null)
                    {
                        this.m_Skill.m_targetPos.x = this.mv.x;
                        this.m_Skill.m_targetPos.y = this.mv.y;
                        this.m_Skill.m_targetPos.z = this.mv.z;
                        this.m_Skill.m_targetPos.face = this.mv.face;
                        this.m_Skill.m_targetLayer = this.m_layer;
                        this.m_Skill.m_vResultList.Clear();

                        this.m_Skill.m_nTargetCount = 0;

                        switch (this.m_Skill.m_SkillBase.effect_type)
                        {
                            case 271:
                                this.FIRE_AREA_EFFECT_MAGIC_DAMAGE(pCaster);
                                break;

//                             case 272:
//                                 this.FIRE_AREA_EFFECT_MAGIC_DAMAGE_AND_HEAL(pCaster);
//                                 break;
// 
//                             case 273:
//                                 this.FIRE_AREA_EFFECT_MAGIC_DAMAGE_AND_HEAL_T2(pCaster);
//                                 break;
// 
//                             case 352:
//                                 this.FIRE_AREA_EFFECT_MAGIC_DAMAGE_OLD(pCaster);
//                                 break;
// 
//                             case 353:
//                                 this.FIRE_AREA_EFFECT_HEAL(pCaster);
//                                 break;
// 
//                             case 382:
//                                 this.FIRE_TRAP_DAMAGE(pCaster);
//                                 break;
// 
//                             case 383:
//                             case 384:
//                                 this.FIRE_TRAP_MULTIPLE_DAMAGE(pCaster);
//                                 break;
// 
//                             case 9503:
//                                 this.FIRE_AREA_EFFECT_HEAL_BY_FIELD_PROP(pCaster);
//                                 break;

                            default:
                                Globals.Log.Error("SkillProp.onProcess - Unkown Effect Type: {0}", this.m_Skill.m_SkillBase.effect_type);
                                break;
                        }

                        if (this.m_Skill.m_nTargetCount != 0)
                        {
                            this.m_Skill.broadcastSkillMessage((int)(this.mv.x / GameContent.g_nRegionSize),(int)(this.mv.y / GameContent.g_nRegionSize),
                                this.m_layer,0,0,4);
                        }
                    }
                    else
                    {
                        this.m_bProcessEnd = true;
                        RappelzServer.Instance.RemoveObject(this);
                        RappelzServer.Instance.DeleteObject(this);
                    }
                }
//                        ArcadiaAutoLock::_ArcadiaAutoLock(&_lock);
                return;
            }

        }

        public override bool IsSkillProp()
        {
            return true;
        }
// Function       :   public int GetSkillId()
// Function       :   public int GetSkillLevel()
// Function       :   public unsigned long GetStartTime()
// Function       :   public unsigned int GetCasterHandle()

        public void PendRemove()
        {
            if (!this.m_bProcessEnd)
            {
                if (!this.m_bIsRemovePended)
                    this.m_bIsRemovePended = true;
            }
        }

        protected void INIT_AREA_EFFECT_MAGIC_DAMAGE()
        {
            uint ct = Globals.GetArTime();
            this.m_Info.m_nStartTime = ct;
            this.m_Info.m_nEndTime = (uint)(((this.m_Skill.m_SkillBase.m_var[3] + (this.m_Skill.m_SkillBase.m_var[4] * this.m_Skill.m_nRequestedSkillLevel)) * 100) + ct);
            this.m_Info.m_nInterval = (uint)(this.m_Skill.m_SkillBase.m_var[6] * 100);

        }

        protected void INIT_AREA_EFFECT_HEAL()
        {
            uint ct = Globals.GetArTime();
            this.m_Info.m_nStartTime = ct;
            this.m_Info.m_nEndTime = (uint)(((this.m_Skill.m_SkillBase.m_var[7] + (this.m_Skill.m_SkillBase.m_var[8] * this.m_Skill.m_nRequestedSkillLevel)) * 100) + ct);
            this.m_Info.m_nInterval = (uint)(this.m_Skill.m_SkillBase.m_var[10] * 100);
        }

        protected void INIT_AREA_EFFECT_HEAL_BY_FIELD_PROP()
        {
            uint ct = Globals.GetArTime();
            this.m_Info.m_nStartTime = ct;
            this.m_Info.m_nEndTime = (uint)(((this.m_Skill.m_SkillBase.m_var[7] + (this.m_Skill.m_SkillBase.m_var[8] * this.m_Skill.m_nRequestedSkillLevel)) * 100) + ct);
            this.m_Info.m_nInterval = (uint)(this.m_Skill.m_SkillBase.m_var[5] * 100);
        }

// Function       :   protected void StructSkillProp::FIRE_AREA_EFFECT_MAGIC_DAMAGE_OLD(struct StructCreature *)
// Function       :   protected void StructSkillProp::FIRE_AREA_EFFECT_HEAL(struct StructCreature *)
// Function       :   protected void StructSkillProp::FIRE_AREA_EFFECT_HEAL_BY_FIELD_PROP(struct StructCreature *)

        protected void INIT_SKILL_PROP_PARAMETER(uint nDuration, uint nInterval)
        {
            uint ct = Globals.GetArTime();
            this.m_Info.m_nLastFireTime = 0;
            this.m_Info.m_nStartTime = ct;
            this.m_Info.m_nEndTime = nDuration + ct;
            this.m_Info.m_nInterval = nInterval;
        }

        protected void FIRE_AREA_EFFECT_MAGIC_DAMAGE(Creature pCaster)
        {
// .text:004DC30D var_8C          = qword ptr -8Ch
// .text:004DC30D var_80          = qword ptr -80h
// .text:004DC30D var_74          = dword ptr -74h
            SkillResult skill_result;                   // -60  -3Ch
            int nDamage;                                // 4    4
            uint t;                                     // 12   0Ch
            Creature.Damage Damage;                     // 16   10h
            List<uint> vResult;                         // 32   20h
            RappelzPair<float,int> HateMod;             // 48   30h
            int nLevel;                                 // 60   3Ch
            int nHate;                                  // 60   3Ch
            Creature pCreature;                         // 68   44h
// .text:004DC30D it              = std::_Vector_iterator<unsigned int,std::allocator<unsigned int> > ptr  48h
            float fRange;                               // 80   50h
            State.StateCode nStateCode;                 // 80   50h
            int elemental_type;                         // 84   54h
            int flag;                                   // 88   58h
// .text:004DC30D pCaster         = 108  6Ch

            vResult = new List<uint>();

            fRange = (this.m_Skill.m_SkillBase.m_var[9] * 12);
            nDamage = (int)((this.m_Skill.m_SkillBase.m_var[10] + (this.m_Skill.m_SkillBase.m_var[11] * this.m_Skill.m_nEnhance)) 
                * ((this.m_Skill.m_SkillBase.m_var[5] * this.m_Skill.m_nEnhance) + this.m_nOwnerMagicPoint 
                * ((this.m_Skill.m_SkillBase.m_var[0] + (this.m_Skill.m_SkillBase.m_var[1] * this.m_Skill.m_nRequestedSkillLevel)) 
                    + (this.m_Skill.m_SkillBase.m_var[2] * this.m_Skill.m_nEnhance))
                + (this.m_Skill.m_SkillBase.m_var[3] + (this.m_Skill.m_SkillBase.m_var[4] * this.m_Skill.m_nRequestedSkillLevel))));
            RappelzServer.Instance.EnumMovableObject(this.mv, this.m_layer, fRange, vResult, true, true);

            t = Globals.GetArTime();

            foreach(uint uid in vResult)
            {
                pCreature = GameObject.raw_get(uid)as Creature;

                if (pCreature != null)
                {
                    if (!pCreature.IsPet())
                    {
                        if (pCaster.IsEnemy(pCreature,true))
                        {
                            if (pCreature.m_nHP != 0)
                            {
                                elemental_type = this.m_Skill.m_SkillBase.elemental;
                                flag = 0;
                                Damage = pCreature.DealMagicalDamage(this.m_Skill.m_Owner,nDamage,(Elemental.Type)elemental_type,
                                    this.m_Skill.m_SkillBase.GetHitBonus(this.m_Skill.m_nEnhance, pCaster.GetLevel()-pCreature.GetLevel()),
                                    this.m_Skill.m_SkillBase.critical_bonus + this.m_Skill.m_nRequestedSkillLevel * this.m_Skill.m_SkillBase.critical_bonus_per_skl,
                                    0,pCreature.m_MagicalSkillStatePenalty,null);

                                if (Damage.bCritical)
                                    flag = 1;
                                if (Damage.bMiss)
                                    flag |= 2;
                                if (Damage.bBlock)
                                    flag |= 4;
                                if (Damage.bPerfectBlock)
                                    flag |= 8;

                                skill_result = new SkillResult(); // -60
                                skill_result.damage = new SkillResult.Damage();
                                skill_result.type = 1;
                                skill_result.damage.type = 1;
                                skill_result.hTarget = pCreature.m_hHandle;
                                skill_result.damage.hTarget = pCreature.m_hHandle; // -59
                                skill_result.damage.target_hp = pCreature.m_nHP;
                                skill_result.damage.damage_type = (byte) elemental_type;
                                skill_result.damage.damage = Damage.nDamage;
                                skill_result.damage.flag = flag;
                                this.m_Skill.m_vResultList.Add(skill_result);
                                ++this.m_Skill.m_nTargetCount;

                                if (!Damage.bMiss)
                                {
                                    if (pCreature.IsMonster())
                                    {
                                        HateMod = pCaster.GetHateMod((int)(this.m_Skill.m_SkillBase.is_spell_act != 0 ? 2 : 1), this.m_Skill.m_SkillBase.is_harmful != 0);
                                        nHate = Damage.nDamage + HateMod.Second;
                                        (pCreature as Monster).AddHate(this.m_Skill.m_Owner.m_hHandle, (int)(this.m_fHateRatio * (nHate * HateMod.First)), true, true);
                                    }
                                    else
                                    {
                                        if (pCreature.IsNPC())
                                            (pCreature as NPC).SetAttacker(this.m_Skill.m_Owner);
                                    }

                                    if (this.m_Skill.m_SkillBase.state_id != 0)
                                    {
                                        nLevel = this.m_Skill.m_SkillBase.GetStateLevel(this.m_Skill.m_nRequestedSkillLevel,this.m_Skill.m_nEnhance);
                                        pCreature.AddState((State.StateType)this.m_Skill.m_SkillBase.state_type,(State.StateCode)this.m_Skill.m_SkillBase.state_id,
                                            pCaster.m_hHandle,nLevel,t,(uint)(t+this.m_Skill.m_SkillBase.GetStateSecond(this.m_Skill.m_nRequestedSkillLevel, this.m_Skill.m_nEnhance)),
                                            false,0,"");
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

// Function       :   protected void StructSkillProp::FIRE_AREA_EFFECT_MAGIC_DAMAGE_AND_HEAL(struct StructCreature *)
// Function       :   protected void StructSkillProp::FIRE_AREA_EFFECT_MAGIC_DAMAGE_AND_HEAL_T2(struct StructCreature *)
// Function       :   protected void StructSkillProp::FIRE_TRAP_DAMAGE(struct StructCreature *)
// Function       :   protected void StructSkillProp::FIRE_TRAP_MULTIPLE_DAMAGE(struct StructCreature *)
// Function       :   public void StructSkillProp(const struct StructSkillProp &)

        private SkillProp(uint caster, Skill pSkill, int nMagicPoint, float fHateRatio)
        {
            uint nDuration;
            uint nInterval;

            this.m_hCaster = caster;
            this.m_Skill = new Skill(pSkill);
            this.m_fHateRatio = fHateRatio;
            this.m_nOwnerMagicPoint = nMagicPoint;
            MemoryPool.AllocMiscHandle(this);

            this.m_nArObjectType = 0;
            this.m_bFired = false;
            this.m_bProcessEnd = false;
            this.m_bIsRemovePended = false;

            switch(this.m_Skill.m_SkillBase.effect_type)
            {
                case 271:
                case 272:
                    nDuration = (uint)((this.m_Skill.m_SkillBase.m_var[6] + (this.m_Skill.m_SkillBase.m_var[7] * this.m_Skill.m_nRequestedSkillLevel)) * 100);
                    nInterval = (uint)(this.m_Skill.m_SkillBase.m_var[8] * 100);
                    this.INIT_SKILL_PROP_PARAMETER(nDuration, nInterval);
                    break;

                case 273:
                    nDuration = (uint)((this.m_Skill.m_SkillBase.m_var[12] + (this.m_Skill.m_SkillBase.m_var[13] * this.m_Skill.m_nRequestedSkillLevel)) * 100);
                    nInterval = (uint)(this.m_Skill.m_SkillBase.m_var[14] * 100);
                    this.INIT_SKILL_PROP_PARAMETER(nDuration, nInterval);
                    break;

                case 352:
                    this.INIT_AREA_EFFECT_MAGIC_DAMAGE();
                    break;

                case 353:
                    this.INIT_AREA_EFFECT_HEAL();
                    break;

                case 382:
                case 383:
                case 384:
                    nDuration = (uint)((this.m_Skill.m_SkillBase.m_var[3] + (this.m_Skill.m_SkillBase.m_var[4] * this.m_Skill.m_nRequestedSkillLevel)) * 100);
                    nInterval = 30;
                    this.INIT_SKILL_PROP_PARAMETER(nDuration, nInterval);
                    break;

                case 9503:
                    this.INIT_AREA_EFFECT_HEAL_BY_FIELD_PROP();
                    break;
                
                default:
                    Globals.Log.Error("SkillProp.onProcess - Unkown Effect Type: {0}", this.m_Skill.m_SkillBase.effect_type);
                    break;

            }
//            XSEH::IncreaseAllocCount("StructSkillProp", 1);
        }

        public uint m_hCaster;                                      // 0xBC
        public SkillPropInfo m_Info = new SkillPropInfo();          // 0xC0 
        public Skill m_Skill;
        public bool m_bFired;                                       // 0x170
        public bool m_bProcessEnd;                                  // 0x171
        public bool m_bIsRemovePended;                              // 0x172
        public int m_nOwnerMagicPoint;                              // 0x174
        public float m_fHateRatio;                                  // 0x178
    }
}
